package sole

import (
	"errors"
	"event-chain/core"
	"event-chain/core/tools"
	"time"

	"github.com/duke-git/lancet/v2/slice"
)

//const (
//	Alter int = iota
//	Reset
//)

type ISoleChain[T any, K comparable] interface {
	Init(func() (T, error)) error
	Register(func(T) T, K, bool, bool) int
	Process()
	Erase(K, int, bool, bool)
}

type Chain[T any, K comparable] struct {
	Data       T
	logger     tools.Logger
	preOptMap  map[K][]IHandler[T]
	postOptMap map[K][]IHandler[T]
	deepCopy   func(T) T
	tier       uint8
}

func NewChain[T any, K comparable](logger tools.Logger, tier uint8, dataCp func(T) T) *Chain[T, K] {
	return &Chain[T, K]{
		logger:     logger,
		deepCopy:   dataCp,
		preOptMap:  make(map[K][]IHandler[T]),
		postOptMap: make(map[K][]IHandler[T]),
		tier:       tier,
	}
}

func (c *Chain[T, K]) Init(init func() (T, error)) error {
	if init == nil {
		return errors.New("init func is nil")
	}
	data, err := init()
	if err != nil {
		c.logger.Errorf("init err: %v", err)
		return err
	}
	c.Data = data
	return nil
}

func (c *Chain[T, K]) Register(handle func(T) T, key K, isOnce, isPre bool) int {
	res := core.InValid
	if handle == nil {
		c.logger.Errorf("register handler is nil")
		return res
	}
	handler := NewHandler(handle, isOnce)
	if isPre {
		c.preOptMap[key] = append(c.preOptMap[key], handler)
		res = len(c.preOptMap[key]) - 1
	} else {
		c.postOptMap[key] = append(c.preOptMap[key], handler)
		res = len(c.postOptMap[key]) - 1
	}
	return res
}

func (c *Chain[T, K]) Erase(key K, index int, isPre, trigger bool) {
	if isPre {
		c.delete(c.preOptMap, key, index)
	} else {
		c.delete(c.postOptMap, key, index)
	}
	if trigger {
		c.Process()
	}
}

func (c *Chain[T, K]) delete(handleMap map[K][]IHandler[T], key K, index int) {
	// expend: 预处理器, 和后置处理器
	switch index {
	case core.InValid:
		return
	case core.DelAll:
		delete(handleMap, key)
	default:
		handleMap[key] = slice.Filter(handleMap[key], func(i int, _ IHandler[T]) bool {
			return i != index
		})
	}
}

func (c *Chain[T, K]) Process() {
	start := time.Now()
	defer func(start time.Time) {
		end := time.Now()
		c.logger.Infof("process end:%s used time: %v", end.Format("2006-01-02 15:04:05.000"), time.Since(start))
	}(start)
	c.logger.Infof("process start:%s", start.Format("2006-01-02 15:04:05.000"))
	data := c.process(c.deepCopy(c.Data), c.preOptMap, c.logger)
	c.Data = c.process(data, c.postOptMap, c.logger)
}

func (c *Chain[T, K]) process(data T, handlerMap map[K][]IHandler[T], logger tools.Logger) T {
	defer func() {
		if err := recover(); err != nil {
			logger.Errorf("process err: %v", err)
		}
	}()
	if handlerMap == nil {
		return data
	}
	eraseKeys := make(map[K][]int)
	for key, handlers := range handlerMap {
		for i, handler := range handlers {
			data = handler.Handle(data)
			if handler.Once() {
				if _, has := eraseKeys[key]; !has {
					eraseKeys[key] = make([]int, 0)
				}
				eraseKeys[key] = append(eraseKeys[key], i)
			}
		}
	}
	for key, ids := range eraseKeys {
		if len(handlerMap[key]) == len(ids) {
			c.delete(handlerMap, key, core.DelAll)
			continue
		}
		for _, i := range ids {
			c.delete(handlerMap, key, i)
		}
	}
	return data
}
